/*
 * Copyright (C) 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/types.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <linux/smsc911x.h>
#include <linux/spi/spi.h>
#include <linux/i2c.h>
#include <linux/ata.h>
#include <linux/pmic_external.h>
#include <linux/ipu.h>
#include <linux/pwm_backlight.h>
#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>

#include <asm/mach/flash.h>
#endif

#include <linux/regulator/consumer.h>
#include <linux/mxcfb.h>
#include <mach/hardware.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
#include <asm/mach/keypad.h>
#include <mach/common.h>
#include <mach/gpio.h>
#include <mach/mmc.h>
#include <mach/mxc_dvfs.h>

#include "devices.h"
#include "iomux.h"
#include "mx51_pins.h"
#include "crm_regs.h"
#include "usb.h"

/*!
 * @file mach-mx51/mx51_3stack.c
 *
 * @brief This file contains the board specific initialization routines.
 *
 * @ingroup MSL_MX51
 */
extern int __init mx51_3stack_init_mc13892(void);
extern void __init mx51_3stack_io_init(void);
extern struct cpu_wp *(*get_cpu_wp)(int *wp);
extern void (*set_num_cpu_wp)(int num);
static int num_cpu_wp = 3;

/* working point(wp): 0 - 800MHz; 1 - 166.25MHz; */
static struct cpu_wp cpu_wp_auto[] = {
	{
	 .pll_rate = 1000000000,
	 .cpu_rate = 1000000000,
	 .pdf = 0,
	 .mfi = 10,
	 .mfd = 11,
	 .mfn = 5,
	 .cpu_podf = 0,
	 .cpu_voltage = 1175000,},
	{
	 .pll_rate = 800000000,
	 .cpu_rate = 800000000,
	 .pdf = 0,
	 .mfi = 8,
	 .mfd = 2,
	 .mfn = 1,
	 .cpu_podf = 0,
	 .cpu_voltage = 1100000,},
	{
	 .pll_rate = 800000000,
	 .cpu_rate = 166250000,
	 .pdf = 4,
	 .mfi = 8,
	 .mfd = 2,
	 .mfn = 1,
	 .cpu_podf = 4,
	 .cpu_voltage = 850000,},
};

struct cpu_wp *mx51_3stack_get_cpu_wp(int *wp)
{
	*wp = num_cpu_wp;
	return cpu_wp_auto;
}

void mx51_3stack_set_num_cpu_wp(int num)
{
	num_cpu_wp = num;
	return;
}

static struct mxc_w1_config mxc_w1_data = {
	.search_rom_accelerator = 1,
};

/*
Inicializacion Keypad
*/
static u16 keymapping[24] = {
	KEY_1, KEY_2, KEY_3, KEY_F1, KEY_UP, KEY_F2,
	KEY_4, KEY_5, KEY_6, KEY_LEFT, KEY_SELECT, KEY_RIGHT,
	KEY_7, KEY_8, KEY_9, KEY_F3, KEY_DOWN, KEY_F4,
	KEY_0, KEY_OK, KEY_ESC, KEY_ENTER, KEY_MENU, KEY_BACK,
};

static struct keypad_data keypad_plat_data = {
	.rowmax = 4,
	.colmax = 6,
	.irq = MXC_INT_KPP,
	.learning = 0,
	.delay = 2,
	.matrix = keymapping,
};

/*
Inicializacion Display
*/

extern int enable_emerging_display;
extern int enable_toshiba_display;

int handle_bl(int brightness)
{
	if (enable_emerging_display)
		/* reversed: max duty cycle is off */
		return (255 - brightness);
	else if (enable_toshiba_display) {
		/* we can switch on/off backlight and LVDS transceiver */
		if (brightness)
			gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_CLK), 1);
		else
			gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_CLK), 0);
		return brightness;
	}
	return 0;
}

static struct platform_pwm_backlight_data mxc_pwm_backlight_data = {
	.pwm_id = 0,
	.max_brightness = 255,
	.dft_brightness = 128,
	/* spec of toshiba display backlight */
	.pwm_period_ns = 5000000,
	.notify = handle_bl,
};

extern void mx5_ipu_reset(void);
static struct mxc_ipu_config mxc_ipu_data = {
	.rev = 2,
	.reset = mx5_ipu_reset,
};

extern void mx5_vpu_reset(void);
static struct mxc_vpu_platform_data mxc_vpu_data = {
	.reset = mx5_vpu_reset,
};

/*
Inicializacion SPI
*/

extern void mx51_3stack_gpio_spi_chipselect_active(int cspi_mode, int status,
						   int chipselect);
extern void mx51_3stack_gpio_spi_chipselect_inactive(int cspi_mode, int status,
						     int chipselect);
static struct mxc_spi_master mxcspi1_data = {
	.maxchipselect = 4,
	.spi_version = 23,
	.chipselect_active = mx51_3stack_gpio_spi_chipselect_active,
	.chipselect_inactive = mx51_3stack_gpio_spi_chipselect_inactive,
};

/*
Inicializacion I2C
*/

static struct mxc_i2c_platform_data mxci2c_data = {
	.i2c_clk = 100000,
};

static struct mxc_i2c_platform_data mxci2c_hs_data = {
	.i2c_clk = 400000,
};

static struct mxc_srtc_platform_data srtc_data = {
	.srtc_sec_mode_addr = 0x83F98840,
};

static struct tve_platform_data tve_data = {
	.dac_reg = "VVIDEO",
	.dig_reg = "VDIG",
};

static struct mxc_dvfs_platform_data dvfs_core_data = {
	.reg_id = "SW1",
	.clk1_id = "cpu_clk",
	.clk2_id = "gpc_dvfs_clk",
	.gpc_cntr_reg_addr = MXC_GPC_CNTR,
	.gpc_vcr_reg_addr = MXC_GPC_VCR,
	.ccm_cdcr_reg_addr = MXC_CCM_CDCR,
	.ccm_cacrr_reg_addr = MXC_CCM_CACRR,
	.ccm_cdhipr_reg_addr = MXC_CCM_CDHIPR,
	.prediv_mask = 0x1F800,
	.prediv_offset = 11,
	.prediv_val = 3,
	.div3ck_mask = 0xE0000000,
	.div3ck_offset = 29,
	.div3ck_val = 2,
	.emac_val = 0x08,
	.upthr_val = 25,
	.dnthr_val = 9,
	.pncthr_val = 33,
	.upcnt_val = 10,
	.dncnt_val = 10,
	.delay_time = 30,
	.num_wp = 3,
};

static struct mxc_dvfsper_data dvfs_per_data = {
	.reg_id = "SW2",
	.clk_id = "gpc_dvfs_clk",
	.gpc_cntr_reg_addr = MXC_GPC_CNTR,
	.gpc_vcr_reg_addr = MXC_GPC_VCR,
	.gpc_adu = 0x0,
	.vai_mask = MXC_DVFSPMCR0_FSVAI_MASK,
	.vai_offset = MXC_DVFSPMCR0_FSVAI_OFFSET,
	.dvfs_enable_bit = MXC_DVFSPMCR0_DVFEN,
	.irq_mask = MXC_DVFSPMCR0_FSVAIM,
	.div3_offset = 0,
	.div3_mask = 0x7,
	.div3_div = 2,
	.lp_high = 1200000,
	.lp_low = 1200000,
};

static struct mxc_spdif_platform_data mxc_spdif_data = {
	.spdif_tx = 1,
	.spdif_rx = 0,
	.spdif_clk_44100 = 0,	/* spdif_ext_clk source for 44.1KHz */
	.spdif_clk_48000 = 7,	/* audio osc source */
	.spdif_clkid = 0,
	.spdif_clk = NULL,	/* spdif bus clk */
};


/* NAND Flash Partitions */
#ifdef CONFIG_MTD_PARTITIONS

static struct mtd_partition nand_flash_partitions[] = {
	{
	 .name = "nand.bootloader",
	 .offset = 0,
	 .size = 3 * 1024 * 1024},
	{
	 .name = "nand.kernel",
	 .offset = MTDPART_OFS_APPEND,
	 .size = 5 * 1024 * 1024},
	{
	 .name = "nand.rootfs",
	 .offset = MTDPART_OFS_APPEND,
	 .size = 1024 * 1024 * 1024},
	{
	 .name = "nand.userfs1",
	 .offset = MTDPART_OFS_APPEND,
	 .size = 1000 * 1024 * 1024},
};

#endif

extern void gpio_nand_active(void);
extern void gpio_nand_inactive(void);

static int nand_init(void)
{
	/* Configure the pins */
	gpio_nand_active();
	return 0;
}

static void nand_exit(void)
{
	/* Free the pins */
	gpio_nand_inactive();
}

static struct flash_platform_data mxc_nand_data = {
	#ifdef CONFIG_MTD_PARTITIONS
		.parts = nand_flash_partitions,
		.nr_parts = ARRAY_SIZE(nand_flash_partitions),
	#endif
	.width = 1,
	.init = nand_init,
	.exit = nand_exit,
};


/*
Inicializacion de Frame Buffers (Salidas de Video)
*/

static struct resource mxcfb_resources[] = {
	[0] = {
		.flags = IORESOURCE_MEM,
	},
};

static struct fb_videomode video_modes[] = {
/*
FIELDS DESCRIPTION:
	{
		name, refresh, xres, yres, pixclock,
		left_margin, right_margin,
		upper_margin, lower_margin,
		hsync_len, vsync_len,
		sync,
		vmode,
		flag,
	},
FORMULAS:
	horiz. total = xres + left_margin + right_margin + hsync_len
	vert. total = yres + upper_margin + lower_margin + vsync_len
*/
	{
		/* PAL TV output */
		"TV-PAL", 50, 720, 576, 74074,
		132, 11,
		22, 26,
		1, 1,
		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_EXT,
		FB_VMODE_INTERLACED | FB_VMODE_ODD_FLD_FIRST,
		0,
	},
	{
		/* NTSC TV output */
		"TV-NTSC", 60, 720, 480, 74074,
		122, 15,
		18, 26,
		1, 1,
		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_EXT,
		FB_VMODE_INTERLACED,
		0,
	},
        {
		/* Hitachi TFT 240x320 */
		"HITACHI-QVGA", 60, 240, 320, 186697,
		11, 17,
		2, 4,
		5, 1,
		FB_SYNC_CLK_LAT_FALL,
		FB_VMODE_NONINTERLACED,
		0,
	},
	{
		/* Emerging TFT 800x480 */
		"EMERGING-WVGA", 60, 800, 480, 33411,
		88, 40,
		33, 10,
		128, 2,
		0,
		FB_VMODE_NONINTERLACED,
		0,
	},
	{
		/* Toshiba LVDS TFT 1024x768 */
		"TOSHIBA-XGA", 60, 1024, 768, 15384,
		136, 24,
		33, 3,
		160, 2,
		FB_SYNC_CLK_LAT_FALL,
		FB_VMODE_NONINTERLACED,
		0,
	},
};

static struct mxc_fb_platform_data fb_data[] = {
	{
	 .interface_pix_fmt = IPU_PIX_FMT_RGB24,
	 .mode = video_modes,
	 .num_modes = ARRAY_SIZE(video_modes),
	 },
	{
	 .interface_pix_fmt = IPU_PIX_FMT_RGB565,
	 .mode = video_modes,
	 .num_modes = ARRAY_SIZE(video_modes),
	 },
};

extern int enable_tvout;
extern char enable_hdmi[];

static int __init mxc_init_fb(void)
{
	int hdmi_primary = 0;

	/* DI0 devices */
	if (strlen(enable_hdmi) != 0) {
		fb_data[0].mode_str = enable_hdmi;
		printk(KERN_INFO "%s: HDMI is primary: %s\n",__FUNCTION__,
		       enable_hdmi);
		hdmi_primary = 1;
	}

	/* DI 1 devices */
	if (enable_emerging_display) {
		/* reset */
		gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_RS), 0);
		msleep(1);
		gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_RS), 1);
		fb_data[1].mode_str = "EMERGING-WVGA";
		printk(KERN_INFO "%s: Emerging WVGA display is %s\n",
		       __FUNCTION__,hdmi_primary ? "secondary" : "primary");
	} else if (enable_toshiba_display) {
		/* backlight and lvds on */
		gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_CLK), 1);
		fb_data[1].mode_str = "TOSHIBA-XGA";
		printk(KERN_INFO "%s: Toshiba XGA display is %s\n",
		       __FUNCTION__,hdmi_primary ? "secondary" : "primary");
	} else if (enable_tvout == 1) {
		/* pal */
		fb_data[1].mode_str = "TV-PAL";
		printk(KERN_INFO "%s: TVOUT (PAL) is %s\n",__FUNCTION__,
		       hdmi_primary ? "secondary" : "primary");
	} else if (enable_tvout == 2) {
		/* ntsc */
		fb_data[1].mode_str = "TV-NTSC";
		printk(KERN_INFO "%s: TVOUT (NTSC) is %s\n",__FUNCTION__,
		       hdmi_primary ? "secondary" : "primary");
	}

	if (hdmi_primary) {
		/* DI0 is primary */

		/* DI0 -> DP-BG channel: */
		mxc_fb_devices[0].num_resources = ARRAY_SIZE(mxcfb_resources);
		mxc_fb_devices[0].resource = mxcfb_resources;
		mxc_register_device(&mxc_fb_devices[0], &fb_data[0]);

		/* DI1 -> DC channel: */
		mxc_register_device(&mxc_fb_devices[1], &fb_data[1]);
	} else if (enable_tvout > 0 || enable_emerging_display
		   || enable_toshiba_display) {
		/* DI1 is primary */

		/* DI1 -> DP-BG channel: */
		mxc_fb_devices[1].num_resources = ARRAY_SIZE(mxcfb_resources);
		mxc_fb_devices[1].resource = mxcfb_resources;
		mxc_register_device(&mxc_fb_devices[1], &fb_data[1]);

		/* DI0 -> DC channel: */
		mxc_register_device(&mxc_fb_devices[0], &fb_data[0]);
	}

	/* DI0/1 DP-FG channel: */
	mxc_register_device(&mxc_fb_devices[2], NULL);

	return 0;
}
device_initcall(mxc_init_fb);

/*
Inicializacion de una camara web OV2655
*/

#if defined(CONFIG_MXC_CAMERA_OV2655) || defined(CONFIG_MXC_CAMERA_OV2655_MODULE)
void ov2655_powerdown(void)
{
	/* high = power down */
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI1_D8), 1);
}
EXPORT_SYMBOL(ov2655_powerdown);

void ov2655_powerup(void)
{
	/* low = not power down */
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI1_D8), 0);
}
EXPORT_SYMBOL(ov2655_powerup);

void ov2655_reset(void)
{
	/* minimum 1 ms */
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), 0);
	msleep(10);
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), 1);
	msleep(10);
}
EXPORT_SYMBOL(ov2655_reset);

static struct mxc_camera_platform_data camera_data = {
	.mclk = 24000000,
	.csi = 0,
};
#endif
/*From here: Added to include TVP5147 support*/
#if defined(CONFIG_MXC_TVIN_TVP5147) || defined(CONFIG_MXC_TVIN_TVP5147_MODULE)
void tvp5147_powerdown(void)
{
	// high = power down
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI1_D8), 1);
}
EXPORT_SYMBOL(tvp5147_powerdown);

void tvp5147_powerup(void)
{
	// low = not power down
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI1_D8), 0);
}
EXPORT_SYMBOL(tvp5147_powerup);

void tvp5147_reset(void)
{
	// minimum 1 ms
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), 0);
	msleep(10);
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), 1);
	msleep(10);
}
EXPORT_SYMBOL(tvp5147_reset);

static void tvp5147_pwdn(int pwdn)
{
	if (pwdn)
		gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI1_D8), 1);
	else
		gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI1_D8), 0);
}

static struct mxc_tvin_platform_data tvp5147_data = {
	.dvddio_reg = NULL,
	.dvdd_reg = NULL,
	.avdd_reg = NULL,
	.pvdd_reg = NULL,
	.pwdn = tvp5147_pwdn,
	.reset = NULL,
};
#endif
/*To here: Added to include TVP5147 support */

/* I2C1 */
static struct i2c_board_info mxc_i2c1_board_info[] __initdata = {
	{ /* ad9889 */
		.type = "ad9889-i2c",
		.addr = 0x39,
		.irq = IOMUX_TO_IRQ(MX51_PIN_DI1_D1_CS),
	},
	{
		.type = "sgtl5000-i2c",
		.addr = 0x0a,
	},
#if defined(CONFIG_MXC_CAMERA_OV2655) || defined(CONFIG_MXC_CAMERA_OV2655_MODULE)
	{ /* OV2655 camera */
		.type = "ov2655",
		.addr = 0x30,
		.platform_data = (void *)&camera_data,
	},
#endif
/*From here: Added to include TVP5147 support */
#if defined(CONFIG_MXC_TVIN_TVP5147) || defined(CONFIG_MXC_TVIN_TVP5147_MODULE)
	{ /* TVP5147 tv decoder */
		.type = "tvp5147",
		.addr = 0x5c,
		.platform_data = (void *)&tvp5147_data,
	},
#endif
/*To here: Added to include TVP5147 support */
};

/* I2C2 */
static struct i2c_board_info mxc_i2c2_board_info[] __initdata = {
};

/*!
 * Get WP pin value to detect write protection
 */
static int sdhc_write_protect(struct device *dev)
{
	unsigned short rc = 0;

	if (to_platform_device(dev)->id == 0)
		rc = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_1));
	else
		rc = 0;
	return rc;
}

static unsigned int sdhc_get_card_det_status(struct device *dev)
{
	int ret;

	if (to_platform_device(dev)->id == 0) {
		ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0));
		return ret;
	} else {		/* config the det pin for SDHC2 */
		return 0;
	}
}

static struct mxc_mmc_platform_data mmc1_data = {
	.ocr_mask = MMC_VDD_31_32,
	.caps = MMC_CAP_4_BIT_DATA,
	.min_clk = 150000,
	.max_clk = 52000000,
	.card_inserted_state = 0,
	.status = sdhc_get_card_det_status,
	.wp_status = sdhc_write_protect,
	.clock_mmc = "esdhc_clk",
	.power_mmc = NULL,
};
static struct mxc_mmc_platform_data mmc2_data = {
	.ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 |
	    MMC_VDD_31_32,
	.caps = MMC_CAP_4_BIT_DATA,
	.min_clk = 150000,
	.max_clk = 50000000,
	.card_inserted_state = 0,
	.status = sdhc_get_card_det_status,
	.wp_status = sdhc_write_protect,
	.clock_mmc = "esdhc_clk",
};

static struct platform_device mxc_sgtl5000_device = {
	.name = "imx-3stack-sgtl5000",
};

static int mxc_sgtl5000_amp_enable(int enable)
{
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A27), enable ? 1 : 0);
	return 0;
}

static struct mxc_audio_platform_data sgtl5000_data = {
	.ssi_num = 1,
	.src_port = 2,
	.ext_port = 3,
	.amp_enable = mxc_sgtl5000_amp_enable,
	.sysclk = 24576000,
};

static void bt_reset(void)
{
	/* reset */
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_9), 0);
	mdelay(100);
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_9), 1);
}

static struct mxc_bt_platform_data mxc_bt_data = {
	.bt_vdd = NULL,
	.bt_vdd_parent = NULL,
	.bt_vusb = NULL,
	.bt_vusb_parent = NULL,
	.bt_reset = bt_reset,
};

static struct platform_device mxc_bt_device = {
	.name = "mxc_bt",
	.id = 0,
};

static void mxc_unifi_hardreset(int pin_level)
{
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D19), pin_level & 0x01);
}

static struct mxc_unifi_platform_data unifi_data = {
	.hardreset = mxc_unifi_hardreset,
	.reg_vdd_vpa = "VSD",
	.reg_1v5_dd = "VGEN1",
	.host_id = 1,
};

struct mxc_unifi_platform_data *get_unifi_plat_data(void)
{
	return &unifi_data;
}
EXPORT_SYMBOL(get_unifi_plat_data);

/*!
 * Board specific fixup function. It is called by \b setup_arch() in
 * setup.c file very early on during kernel starts. It allows the user to
 * statically fill in the proper values for the passed-in parameters. None of
 * the parameters is used currently.
 *
 * @param  desc         pointer to \b struct \b machine_desc
 * @param  tags         pointer to \b struct \b tag
 * @param  cmdline      pointer to the command line
 * @param  mi           pointer to \b struct \b meminfo
 */
static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
				   char **cmdline, struct meminfo *mi)
{
	char *str;
	struct tag *t;
	struct tag *mem_tag = 0;
	int total_mem = SZ_512M;
	int left_mem = 0;
	int gpu_mem = SZ_64M;
	int fb_mem = SZ_32M;

	mxc_set_cpu_type(MXC_CPU_MX51);

	get_cpu_wp = mx51_3stack_get_cpu_wp;
	set_num_cpu_wp = mx51_3stack_set_num_cpu_wp;

	for_each_tag(mem_tag, tags) {
		if (mem_tag->hdr.tag == ATAG_MEM) {
			total_mem = mem_tag->u.mem.size;
			left_mem = total_mem - gpu_mem - fb_mem;
			break;
		}
	}

	for_each_tag(t, tags) {
		if (t->hdr.tag == ATAG_CMDLINE) {
			str = t->u.cmdline.cmdline;
			str = strstr(str, "mem=");
			if (str != NULL) {
				str += 4;
				left_mem = memparse(str, &str);
				if (left_mem == 0 || left_mem > total_mem)
					left_mem = total_mem - gpu_mem - fb_mem;
			}
			str = t->u.cmdline.cmdline;
			str = strstr(str, "gpu_memory=");
			if (str != NULL) {
				str += 11;
				gpu_mem = memparse(str, &str);
			}
			break;
		}
	}

	if (mem_tag) {
		fb_mem = total_mem - left_mem - gpu_mem;
		if (fb_mem < 0) {
			gpu_mem = total_mem - left_mem;
			fb_mem = 0;
		}
		mem_tag->u.mem.size = left_mem;

		/*reserve memory for gpu*/
		gpu_device.resource[5].start =
			mem_tag->u.mem.start + left_mem;
		gpu_device.resource[5].end =
			gpu_device.resource[5].start + gpu_mem - 1;
#if defined(CONFIG_FB_MXC_SYNC_PANEL) ||		\
	defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
		if (fb_mem) {
			mxcfb_resources[0].start =
				gpu_device.resource[5].end + 1;
			mxcfb_resources[0].end =
				mxcfb_resources[0].start + fb_mem - 1;
		} else {
			mxcfb_resources[0].start = 0;
			mxcfb_resources[0].end = 0;
		}
#endif
	}
}

static struct blt_gps_platform_data gps_data = {
	.reg = "VAUDIO",
	.uV = 3000000,
};

static struct platform_device blt_gps_device = {
	.name = "blt_gps",
	.id = -1,
};

static void blt_gsm_power_on(int on)
{
	if (on) {
		/* power_on low */
		gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D22), 0);
		/* out of reset (module pulls down reset in off state) */
		gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D23), 1);
	} else {
		/* power_on high */
		gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D22), 1);
		/* reset low (module pulls down reset in off state) */
		gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D23), 0);
	}
}

static void blt_gsm_reset(void)
{
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D23), 0);
	msleep(100); /* at least 50ms */
	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D23), 1);
}

static struct blt_gsm_platform_data gsm_data = {
	.power = blt_gsm_power_on,
	.reset = blt_gsm_reset,
};

static struct platform_device blt_gsm_device = {
	.name = "blt_gsm",
	.id = -1,
};

#if defined(CONFIG_MXC_CAMERA_OV2655) || defined(CONFIG_MXC_CAMERA_OV2655_MODULE)
static void start_mclk(void)
{
	struct clk *clk;
	uint32_t freq = 0;
	clk = clk_get(NULL, "csi_mclk1");
	freq = clk_round_rate(clk, camera_data.mclk);
	clk_set_rate(clk, freq);
	clk_enable(clk);
	clk_put(clk);
}
#endif
/*From here: Added to include TVP5147 support */
#if defined(CONFIG_MXC_TVIN_TVP5147) || defined(CONFIG_MXC_TVIN_TVP5147_MODULE)
static void start_mclk(void)
{
	struct clk *clk;
	uint32_t freq = 0;
	clk = clk_get(NULL, "csi_mclk1");
	freq = clk_round_rate(clk, 15000000);
	clk_set_rate(clk, freq);
	clk_enable(clk);
	clk_put(clk);
}
#endif
/*To here: Added to include TVP5147 support */

/*!
 * Board specific initialization.
 */
static void __init mxc_board_init(void)
{
	mxc_ipu_data.di_clk[0] = clk_get(NULL, "ipu_di0_clk");
	mxc_ipu_data.di_clk[1] = clk_get(NULL, "ipu_di1_clk");

	mxc_spdif_data.spdif_core_clk = clk_get(NULL, "spdif_xtal_clk");
	clk_put(mxc_spdif_data.spdif_core_clk);

	mxc_cpu_common_init();
	mxc_register_gpios();
	mx51_3stack_io_init();

	mxc_register_device(&mxc_dma_device, NULL);
	mxc_register_device(&mxc_wdt_device, NULL);
	mxc_register_device(&mxcspi1_device, &mxcspi1_data);
	mxc_register_device(&mxci2c_devices[0], &mxci2c_data);
	mxc_register_device(&mxci2c_devices[1], &mxci2c_data);
	mxc_register_device(&mxci2c_hs_device, &mxci2c_hs_data);
	mxc_register_device(&mxc_rtc_device, &srtc_data);
	mxc_register_device(&mxc_w1_master_device, &mxc_w1_data);
	mxc_register_device(&mxc_ipu_device, &mxc_ipu_data);
	mxc_register_device(&mxc_tve_device, &tve_data);
	mxc_register_device(&mxcvpu_device, &mxc_vpu_data);
	mxc_register_device(&gpu_device, NULL);
	mxc_register_device(&mxcscc_device, NULL);
	mxc_register_device(&mx51_lpmode_device, NULL);
	mxc_register_device(&busfreq_device, NULL);
	mxc_register_device(&sdram_autogating_device, NULL);
	mxc_register_device(&mxc_dvfs_core_device, &dvfs_core_data);
	mxc_register_device(&mxc_dvfs_per_device, &dvfs_per_data);
	mxc_register_device(&mxc_iim_device, NULL);
	mxc_register_device(&mxc_pwm1_device, NULL);
	mxc_register_device(&mxc_pwm_backlight_device, &mxc_pwm_backlight_data);
	mxc_register_device(&mxc_keypad_device, &keypad_plat_data);
	mxcsdhc1_device.resource[2].start = IOMUX_TO_IRQ(MX51_PIN_GPIO1_0);
	mxcsdhc1_device.resource[2].end = IOMUX_TO_IRQ(MX51_PIN_GPIO1_0);
	mxc_register_device(&mxcsdhc1_device, &mmc1_data);
	mxc_register_device(&mxcsdhc2_device, &mmc2_data);
	mxc_register_device(&mxc_ssi1_device, NULL);
	mxc_register_device(&mxc_ssi2_device, NULL);
	mxc_register_device(&mxc_alsa_spdif_device, &mxc_spdif_data);
	mxc_register_device(&mxc_fec_device, NULL);
	mxc_register_device(&mxc_nandv2_mtd_device, &mxc_nand_data);

	mx51_3stack_init_mc13892();

	i2c_register_board_info(0, mxc_i2c1_board_info,
				ARRAY_SIZE(mxc_i2c1_board_info));
	i2c_register_board_info(1, mxc_i2c2_board_info,
				ARRAY_SIZE(mxc_i2c2_board_info));

	mxc_register_device(&mxc_sgtl5000_device, &sgtl5000_data);
	mxc_register_device(&mxc_bt_device, &mxc_bt_data);
	mxc_register_device(&blt_gps_device, &gps_data);
	mxc_register_device(&blt_gsm_device, &gsm_data);

	mx5_usb_dr_init();
	mx5_usbh1_init();

#if defined(CONFIG_MXC_CAMERA_OV2655) || defined(CONFIG_MXC_CAMERA_OV2655_MODULE)
	start_mclk();
#endif
/*From here: Added to include TVP5147 support */
#if defined(CONFIG_MXC_TVIN_TVP5147) || defined(CONFIG_MXC_TVIN_TVP5147_MODULE)
	start_mclk();
#endif
/*To here: Added to include TVP5147 support */
}

static void __init mx51_3stack_timer_init(void)
{
	struct clk *uart_clk;

	/* Change the CPU voltages for TO2*/
	if (cpu_is_mx51_rev(CHIP_REV_2_0) <= 1) {
		cpu_wp_auto[0].cpu_voltage = 1175000;
		cpu_wp_auto[1].cpu_voltage = 1100000;
		cpu_wp_auto[2].cpu_voltage = 1000000;
	}

	mx51_clocks_init(32768, 24000000, 22579200, 24576000);

	uart_clk = clk_get(NULL, "uart_clk.0");
	early_console_setup(UART1_BASE_ADDR, uart_clk);
}

static struct sys_timer mxc_timer = {
	.init	= mx51_3stack_timer_init,
};

/*
 * The following uses standard kernel macros define in arch.h in order to
 * initialize __mach_desc_MX51_3STACK data structure.
 */
/* *INDENT-OFF* */
MACHINE_START(MX51_3DS, "Bluetechnix SBC-i.MX51")
	/* Maintainer: Freescale Semiconductor, Inc. */
	.phys_io = AIPS1_BASE_ADDR,
	.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
	.fixup = fixup_mxc_board,
	.map_io = mx5_map_io,
	.init_irq = mx5_init_irq,
	.init_machine = mxc_board_init,
	.timer = &mxc_timer,
MACHINE_END
